{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# DMA tutorial: DMA to streamed interfaces\n",
    "\n",
    "Overlay consists of two DMAs and an AXI Stream FIFO (input and output AXI stream interfaces). The FIFO represents an accelerator. A single DMA could be used with a read and write channel enabled, but for demonstration purposes, two different DMAs will be used. \n",
    "\n",
    "* The first DMA with read channel enabled is connected from DDR to IP input stream (reading from DDR, and sending to AXI stream).\n",
    "* The second DMA has a write channel enabled and is connected to IP output stream to DDR (receiving from AXI stream, and writing to DDR memory).\n",
    "\n",
    "\n",
    "![](images/dma_stream_example.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Downloading overlay\n",
    "The overlay can be downloaded automatically when instantiating an overlay class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import Overlay"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "overlay = Overlay(\"./bitstream/dma_tutorial.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can check the IPs in this overlay. Notice the DMAs *axi_dma_from_pl_to_ps* and *axi_dma_from_pl_to_ps*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "overlay.ip_dict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Create DMA instances\n",
    "\n",
    "Using the labels for the DMAs listed above, we can create two DMA objects."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pynq.lib.dma\n",
    "\n",
    "dma_send = overlay.axi_dma_from_ps_to_pl\n",
    "dma_recv = overlay.axi_dma_from_pl_to_ps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Read DMA\n",
    "We will read some data from memory, and write to FIFO in the following cells.\n",
    "\n",
    "The first step is to create the a contiguous memory block. Xlnk will be used to allocate the buffer, and NumPy will be used to specify the type of the buffer. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import allocate\n",
    "import numpy as np\n",
    "\n",
    "data_size = 100\n",
    "\n",
    "input_buffer = allocate(shape=(data_size,), dtype=np.uint32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The array can be used like any other NumPy array. We can write some test data to the array. Later the data will be transferred by the DMA to the FIFO. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(data_size):\n",
    "    input_buffer[i] = i + 0xcafe0000"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's check the contents of the array. The data in the following cell will be sent from PS (DDR memory) to PL (streaming FIFO).\n",
    "\n",
    "### Print first few values of buffer "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "for i in range(10):\n",
    "    print(hex(input_buffer[i]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are ready to carry out DMA transfer from a memory block in DDR to FIFO."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dma_send.sendchannel.transfer(input_buffer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Write DMA\n",
    "Let's read the data back from FIFO stream, and write to MM memory. The steps are similar.\n",
    "\n",
    "We will prepare an empty array before reading data back from FIFO.\n",
    "\n",
    "### Print first few values of buffer "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "output_buffer = allocate(shape=(data_size,), dtype=np.uint32)\n",
    "\n",
    "for i in range(10):\n",
    "    print('0x' + format(output_buffer[i], '02x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dma_recv.recvchannel.transfer(output_buffer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next cell will print out the data received from PL (streaming FIFO) to PS (DDR memory). This should be the same as the data we sent previously.\n",
    "\n",
    "### Print first few values of buffer "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(10):\n",
    "    print('0x' + format(output_buffer[i], '02x'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Free all the memory buffers\n",
    "Don't forget to free the memory buffers to avoid memory leaks!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "del input_buffer, output_buffer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Appendix\n",
    "\n",
    "## AXI DMA pdf\n",
    "\n",
    "PDFs can be linked from, or embedded in a Jupyter notebook. The pdf for the AXI DMA documentation is embedded in this notebook below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import IFrame\n",
    "IFrame(\"pg021_axi_dma.pdf\", width=800, height=800)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
